retire some csv char based routines. (#790)
authortsteven4 <13596209+tsteven4@users.noreply.github.com>
Mon, 13 Dec 2021 16:27:55 +0000 (09:27 -0700)
committerGitHub <noreply@github.com>
Mon, 13 Dec 2021 16:27:55 +0000 (09:27 -0700)
* retire some csv char based routines.

The last csv_lineparse user, garmin_txt, is converted to csv_linesplit.

xcsv_parse_val converted to use csv_stringtrim(QString,QString,int),
eliminating the other overloads.

xcsv_parse_style_line parsing of FIELD_DELIMITER, FIELD_ENCLOSER,
RECORD_DELIMITER and BADCHARS updated to use the above overload of
csv_stringtrim.  FIELD_DELIMITER, FIELD_ENCLOSER and RECORD_DELIMITER
changed to trim, with a double quote enclosure,  before character substitution.
There was a subtle bug in csvs_stringtrim(char, char, int).  When trimming
a source that was all white space the first white space character would
be retained, while others would be trimmed.  This did occur with CRNEWLINE,
but the bug allowed other substitutions to work, e.g. SPACE, NEWLINE, TAB, CR.

A small bug in csv_stringclean was fixed.  If the to_nuke was empty an invalid
regular expression was created.

* save a few string conversions.

* and a few more conversions saved.

* review comments incorporated in garmin_txt reader.

* fix minor bug accumulating badchars.

csv_util.cc
csv_util.h
garmin_txt.cc
xcsv.cc
xcsv.h

index 0a5d986cc5b15db65d50e27adff6719c72f69d39..f522bee69316056532f3b20d8acd2c3c12307417 100644 (file)
@@ -52,80 +52,15 @@ QString
 csv_stringclean(const QString& source, const QString& to_nuke)
 {
   QString r = source;
-  // avoid problematic regular rexpressions, e.g. xmapwpt generated [:\n:],
-  // or one can imagine [0-9] when we meant the characters, '0', '-', and '9',
-  // or one can imagine [^a] when we meant the characters '^' and 'a'.
-  QRegularExpression regex = QRegularExpression(QString("[%1]").arg(QRegularExpression::escape(to_nuke)));
-  assert(regex.isValid());
-  return r.remove(regex);
-}
-
-// csv_stringtrim() - trim whitespace and leading and trailing
-//                    enclosures (quotes)
-//                    returns a copy of the modified string
-//    usage: p = csv_stringtrim(string, "\"", 0)
-char*
-csv_stringtrim(const char* string, const char* enclosure, int strip_max)
-{
-  static const char* p1 = nullptr;
-  char* tmp = xstrdup(string);
-  size_t elen;
-
-  if (!strlen(string)) {
-    return (tmp);
-  }
-
-  if (!enclosure) {
-    elen = 0;
-  } else {
-    elen = strlen(enclosure);
-  }
-
-  char* p2 = tmp + strlen(tmp) - 1;
-  p1 = tmp;
-
-  /* trim off trailing whitespace */
-  while ((p2 > p1) && isspace(*p2)) {
-    p2--;
-  }
-
-  /* advance p1 past any leading whitespace */
-  while ((p1 < p2) && (isspace(*p1))) {
-    p1++;
-  }
-
-  /* if no maximum strippage, assign a reasonable value to max */
-  strip_max = strip_max ? strip_max : 9999;
-
-  /* if we have enclosures, skip past them in pairs */
-  if (elen) {
-    int stripped = 0;
-    while (
-      (stripped < strip_max) &&
-      ((size_t)(p2 - p1 + 1) >= (elen * 2)) &&
-      (strncmp(p1, enclosure, elen) == 0) &&
-      (strncmp((p2 - elen + 1), enclosure, elen) == 0)) {
-      p2 -= elen;
-      p1 += elen;
-      stripped++;
-    }
-  }
-
-  /* copy what's left over back into tmp. */
-  memmove(tmp, p1, (p2 - p1) + 1);
-
-  tmp[(p2 - p1) + 1] = '\0';
-
-  return (tmp);
-}
-
-// Is this really the replacement for the above? No.
-QString
-csv_stringtrim(const QString& source, const QString& enclosure)
-{
-  QString r = source;
-  r.replace(enclosure, "");
-  return r.trimmed();
+  if (!to_nuke.isEmpty()) {
+    // avoid problematic regular rexpressions, e.g. xmapwpt generated [:\n:],
+    // or one can imagine [0-9] when we meant the characters, '0', '-', and '9',
+    // or one can imagine [^a] when we meant the characters '^' and 'a'.
+    QRegularExpression regex = QRegularExpression(QString("[%1]").arg(QRegularExpression::escape(to_nuke)));
+    assert(regex.isValid());
+    r.remove(regex);
+  }
+  return r;
 }
 
 // csv_stringtrim() - trim whitespace and leading and trailing
@@ -212,121 +147,6 @@ csv_dequote(const QString& string, const QString& enclosure)
 
   return retval;
 }
-/*****************************************************************************/
-/* csv_lineparse() - extract data fields from a delimited string. designed   */
-/*                   to handle quoted and delimited data within quotes.      */
-/*                   returns temporary COPY of delimited data field (use it  */
-/*                   or lose it on the next call).                           */
-/*    usage: p = csv_lineparse(string, ",", "\"", line)  [initial call]      */
-/*           p = csv_lineparse(NULL, ",", "\"", line)    [subsequent calls]  */
-/*****************************************************************************/
-char*
-csv_lineparse(const char* stringstart, const char* delimited_by,
-              const char* enclosed_in, const int line_no)
-{
-  static const char* p = nullptr;
-  static char* tmp = nullptr;
-  size_t dlen = 0, elen = 0, efound = 0;
-  int enclosedepth = 0;
-  short int hyper_whitespace_delimiter = 0;
-
-  if (tmp) {
-    xfree(tmp);
-    tmp = nullptr;
-  }
-
-  if (strcmp(delimited_by, "\\w") == 0) {
-    hyper_whitespace_delimiter = 1;
-  }
-
-  /*
-   * This is tacky.  Our "csv" format is actually "commaspace" format.
-   * Changing that causes unwanted churn, but it also makes "real"
-   * comma separated data (such as likely to be produced by Excel, etc.)
-   * unreadable.   So we silently change it here on a read and let the
-   * whitespace eater consume the space.
-   */
-  if (strcmp(delimited_by, ", ") == 0) {
-    delimited_by = ",";
-  }
-
-  if (!p) {
-    /* first pass thru */
-    p =  stringstart;
-
-    if (!p) {
-      /* last pass out */
-      return (nullptr);
-    }
-  }
-
-  /* the beginning of the string we start with (this pass) */
-  const char* sp = p;
-
-  /* length of delimiters and enclosures */
-  if ((delimited_by) && (!hyper_whitespace_delimiter)) {
-    dlen = strlen(delimited_by);
-  }
-  if (enclosed_in) {
-    elen = strlen(enclosed_in);
-  }
-  short int dfound = 0;
-
-  while ((*p) && (!dfound)) {
-    if ((elen) && (strncmp(p, enclosed_in, elen) == 0)) {
-      efound = 1;
-      p+=elen;
-      if (enclosedepth) {
-        enclosedepth--;
-      } else {
-        enclosedepth++;
-      }
-      continue;
-    }
-
-    if (!enclosedepth) {
-      if ((dlen) && (strncmp(p, delimited_by, dlen) == 0)) {
-        dfound = 1;
-      } else if ((hyper_whitespace_delimiter) && (ISWHITESPACE(*p))) {
-        dfound = 1;
-        while (ISWHITESPACE(*p)) {
-          p++;
-        }
-      } else {
-        p++;
-      }
-    } else {
-      p++;
-    }
-  }
-
-  /* allocate enough space for this data field */
-  tmp = (char*) xcalloc((p - sp) + 1, sizeof(char));
-
-  strncpy(tmp, sp, (p - sp));
-  tmp[p - sp] = '\0';
-
-  if (elen && efound) {
-    char* c = csv_stringtrim(tmp, enclosed_in, 0);
-    xfree(tmp);
-    tmp = c;
-  }
-
-  if (dfound) {
-    /* skip over the delimited_by */
-    p += dlen;
-  } else {
-    /* end of the line */
-    p = nullptr;
-  }
-
-  if (enclosedepth != 0) {
-    warning(MYNAME
-            ": Warning- Unbalanced Field Enclosures (%s) on line %d\n",
-            enclosed_in, line_no);
-  }
-  return (tmp);
-}
 
 /*****************************************************************************/
 /* csv_linesplit() - extract data fields from a delimited string. designed   */
index 4ea9fa0be85d72af2a4e0c5c1b3eb84f3baa22e6..c0461bb24546ca146b168b612bbfb1c115b6bca3 100644 (file)
 QString
 csv_stringclean(const QString& source, const QString& to_nuke);
 
-char*
-csv_stringtrim(const char* string, const char* enclosure, int strip_max);
-QString
-csv_stringtrim(const QString& source, const QString& enclosure);
 QString
 csv_stringtrim(const QString& string, const QString& enclosure, int strip_max);
 QString
@@ -45,8 +41,6 @@ csv_dequote(const QString& string, const QString& enclosure);
 
 enum class CsvQuoteMethod {historic, rfc4180};
 
-char*
-csv_lineparse(const char* stringstart, const char* delimited_by, const char* enclosed_in, int line_no);
 QStringList
 csv_linesplit(const QString& string, const QString& delimited_by,
               const QString& enclosed_in, const int line_no, CsvQuoteMethod method = CsvQuoteMethod::historic);
index f4998411220414ca2a413ff97b84127284ff1577..d6a71236a140b8e8f9d465aee1ed63398d7c1bb4 100644 (file)
 #include <QChar>                   // for QChar, QChar::Other_Control
 #include <QIODevice>               // for QIODevice, QIODevice::ReadOnly, QIODevice::WriteOnly
 #include <QString>                 // for QString, operator!=
+#include <QStringList>             // for QStringList
 #include <QTextStream>             // for QTextStream
 #include <QVector>                 // for QVector
 #include <Qt>                      // for CaseInsensitive
 #include <QtGlobal>                // for qPrintable
 
-#include "csv_util.h"              // for csv_lineparse
+#include "csv_util.h"              // for csv_linesplit
+#include "formspec.h"              // for FormatSpecificDataList
 #include "garmin_fs.h"             // for garmin_fs_t, garmin_fs_alloc, garmin_fs_convert_category, GMSD_SECTION_CATEGORIES
 #include "garmin_tables.h"         // for gt_display_modes_e, gt_find_desc_from_icon_number, gt_find_icon_number_from_desc, gt_get_mps_grid_longname, gt_lookup_datum_index, gt_lookup_grid_type, GDB, gt_get_icao_cc, gt_get_icao_country, gt_get_mps_datum_name, gt_waypt_class_names, GT_DISPLAY_MODE...
 #include "inifile.h"               // for inifile_readstr
@@ -123,7 +125,6 @@ static int header_ct[unknown_header + 1];
 /* macros */
 
 #define IS_VALID_ALT(a) (((a) != unknown_alt) && ((a) < GARMIN_UNKNOWN_ALT))
-#define DUPSTR(a) (((a) != NULL) && ((a)[0] != 0)) ? ((a)) : NULL
 
 static char* opt_datum = nullptr;
 static char* opt_dist = nullptr;
@@ -883,76 +884,63 @@ free_header(const header_type ht)
 
 /* data parsers */
 
-static int
-parse_date_and_time(char* str, time_t* value)
+static bool
+parse_date_and_time(const QString& str, time_t* value)
 {
   struct tm tm;
 
   memset(&tm, 0, sizeof(tm));
-  char* cin = lrtrim(str);
-  if (*cin == '\0') {
-    return 0;
+  QString tstr = str.trimmed();
+  if (tstr.isEmpty()) {
+    return false;
   }
 
+  const QByteArray ba = tstr.toUtf8();
+  const char* cin = ba.constData();
   char* cerr = strptime(cin, date_time_format, &tm);
   if (cerr == nullptr) {
     cerr = strptime(cin, "%m/%d/%Y %I:%M:%S %p", &tm);
-    is_fatal(cerr == nullptr, MYNAME ": Invalid date or/and time \"%s\" at line %d!", cin, current_line);
+    is_fatal(cerr == nullptr, MYNAME ": Invalid date or/and time \"%s\" at line %d!", qPrintable(tstr), current_line);
   }
 
 //     printf(MYNAME "_parse_date_and_time: %02d.%02d.%04d, %02d:%02d:%02d\n",
 //             tm.tm_mday, tm.tm_mon+1, tm.tm_year+1900, tm.tm_hour, tm.tm_min, tm.tm_sec);
 
   *value = mklocaltime(&tm);
-  return 1;
+  return true;
 }
 
 static uint16_t
-parse_categories(const char* str)
+parse_categories(const QString& str)
 {
-  char buff[256];
-  uint16_t val;
   uint16_t res = 0;
-  char* cx;
-
-  if (*str == '\0') {
-    return 0;
-  }
-
-  strncpy(buff, str, sizeof(buff));
-  char* cin = lrtrim(buff);
-  if (*cin == '\0') {
-    return 0;
-  }
-
-  strcat(cin, ",");
 
-  while ((cx = strchr(cin, ','))) {
-    *cx++ = '\0';
-    cin = lrtrim(cin);
-    if (*cin != '\0') {
-      if (!garmin_fs_convert_category(cin, &val)) {
-        warning(MYNAME ": Unable to convert category \"%s\" at line %d!\n", cin, current_line);
+  const QStringList catstrings = str.split(',');
+  for (const auto& catstring : catstrings) {
+    QString cin = catstring.trimmed();
+    if (!cin.isEmpty()) {
+      uint16_t val;
+      if (!garmin_fs_convert_category(CSTR(cin), &val)) {
+        warning(MYNAME ": Unable to convert category \"%s\" at line %d!\n", qPrintable(cin), current_line);
       } else {
         res = res | val;
       }
     }
-    cin = cx;
   }
   return res;
 }
 
-static int
-parse_temperature(const char* str, double* temperature)
+static bool
+parse_temperature(const QString& str, double* temperature)
 {
   double value;
   unsigned char unit;
 
-  if ((str == nullptr) || (*str == '\0')) {
-    return 0;
+  if (str.isEmpty()) {
+    return false;
   }
 
-  if (sscanf(str, "%lf %c", &value, &unit) == 2) {
+  if (sscanf(CSTR(str), "%lf %c", &value, &unit) == 2) {
     unit = toupper(unit);
     switch (unit) {
     case 'C':
@@ -964,22 +952,22 @@ parse_temperature(const char* str, double* temperature)
     default:
       fatal(MYNAME ": Unknown temperature unit \"%c\" at line %d!\n", unit, current_line);
     }
-    return 1;
+    return true;
   } else {
-    fatal(MYNAME ": Invalid temperature \"%s\" at line %d!\n", str, current_line);
+    fatal(MYNAME ": Invalid temperature \"%s\" at line %d!\n", qPrintable(str), current_line);
   }
-  return 0;
+  return false;
 }
 
 static void
-parse_header()
+parse_header(const QStringList& lineparts)
 {
-  char* str;
   int column = -1;
 
   free_header(unknown_header);
 
-  while ((str = csv_lineparse(nullptr, "\t", "", column++))) {
+  for (const auto& str : lineparts) {
+    column++;
     header_lines[unknown_header][column] = strupper(xstrdup(str));
     header_ct[unknown_header]++;
     if (header_ct[unknown_header] >= MAX_HEADER_FIELDS) {
@@ -988,21 +976,21 @@ parse_header()
   }
 }
 
-static int
-parse_display(const char* str, int* val)
+static bool
+parse_display(const QString& str, int* val)
 {
-  if ((str == nullptr) || (*str == '\0')) {
-    return 0;
+  if (str.isEmpty()) {
+    return false;
   }
 
   for (gt_display_modes_e i = GT_DISPLAY_MODE_MIN; i <= GT_DISPLAY_MODE_MAX; ++i) {
     if (case_ignore_strcmp(str, gt_display_mode_names[i]) == 0) {
       *val = i;
-      return 1;
+      return true;
     }
   }
-  warning(MYNAME ": Unknown display mode \"%s\" at line %d.\n", str, current_line);
-  return 0;
+  warning(MYNAME ": Unknown display mode \"%s\" at line %d.\n", qPrintable(str), current_line);
+  return false;
 }
 
 static void
@@ -1049,41 +1037,39 @@ bind_fields(const header_type ht)
 }
 
 static void
-parse_grid()
+parse_grid(const QStringList& lineparts)
 {
-  char* str = csv_lineparse(nullptr, "\t", "", 1);
-
-  if (str != nullptr) {
-    if (strstr(str, "dd.ddddd") != nullptr) {
-      grid_index = grid_lat_lon_ddd;
-    } else if (strstr(str, "mm.mmm") != nullptr) {
-      grid_index = grid_lat_lon_dmm;
-    } else if (strstr(str, "mm'ss.s") != nullptr) {
-      grid_index = grid_lat_lon_dms;
-    } else {
-      grid_index = gt_lookup_grid_type(str, MYNAME);
-    }
-  } else {
+  if (lineparts.size() < 1) {
     fatal(MYNAME ": Missing grid headline!\n");
   }
+
+  const QByteArray ba = lineparts.at(0).toUtf8();
+  const char* str = ba.constData();
+  if (strstr(str, "dd.ddddd") != nullptr) {
+    grid_index = grid_lat_lon_ddd;
+  } else if (strstr(str, "mm.mmm") != nullptr) {
+    grid_index = grid_lat_lon_dmm;
+  } else if (strstr(str, "mm'ss.s") != nullptr) {
+    grid_index = grid_lat_lon_dms;
+  } else {
+    grid_index = gt_lookup_grid_type(str, MYNAME);
+  }
 }
 
 static void
-parse_datum()
+parse_datum(const QStringList& lineparts)
 {
-  char* str = csv_lineparse(nullptr, "\t", "", 1);
-
-  if (str != nullptr) {
-    datum_index = gt_lookup_datum_index(str, MYNAME);
-  } else {
+  if (lineparts.size() < 1) {
     fatal(MYNAME ": Missing GPS datum headline!\n");
   }
+
+  const auto& str = lineparts.at(0);
+  datum_index = gt_lookup_datum_index(CSTR(str), MYNAME);
 }
 
 static void
-parse_waypoint()
+parse_waypoint(const QStringList& lineparts)
 {
-  char* str;
   int column = -1;
 
   bind_fields(waypt_header);
@@ -1092,17 +1078,22 @@ parse_waypoint()
   garmin_fs_t* gmsd = garmin_fs_alloc(-1);
   wpt->fs.FsChainAdd(gmsd);
 
-  while ((str = csv_lineparse(nullptr, "\t", "", column++))) {
+  for (const auto& str : lineparts) {
+    column++;
     int i;
     double d;
     int field_no = header_fields[waypt_header][column];
 
     switch (field_no) {
     case  1:
-      wpt->shortname = DUPSTR(str);
+      if (!str.isEmpty()) {
+        wpt->shortname = str;
+      }
       break;
     case  2:
-      wpt->notes = DUPSTR(str);
+      if (!str.isEmpty()) {
+        wpt->notes = str;
+      }
       break;
     case  3:
       for (i = 0; i <= gt_waypt_class_map_line; i++) {
@@ -1183,19 +1174,21 @@ parse_waypoint()
 }
 
 static void
-parse_route_header()
+parse_route_header(const QStringList& lineparts)
 {
-  char* str;
   int column = -1;
 
   auto* rte = new route_head;
 
   bind_fields(route_header);
-  while ((str = csv_lineparse(nullptr, "\t", "", column++))) {
+  for (const auto& str : lineparts) {
+    column++;
     int field_no = header_fields[route_header][column];
     switch (field_no) {
     case 1:
-      rte->rte_name = DUPSTR(str);
+      if (!str.isEmpty()) {
+        rte->rte_name = str;
+      }
       break;
     case 5:
       rte->rte_urls.AddUrlLink(UrlLink(str));
@@ -1207,18 +1200,20 @@ parse_route_header()
 }
 
 static void
-parse_track_header()
+parse_track_header(const QStringList& lineparts)
 {
-  char* str;
   int column = -1;
 
   bind_fields(track_header);
   auto* trk = new route_head;
-  while ((str = csv_lineparse(nullptr, "\t", "", column++))) {
+  for (const auto& str : lineparts) {
+    column++;
     int field_no = header_fields[track_header][column];
     switch (field_no) {
     case 1:
-      trk->rte_name = DUPSTR(str);
+      if (!str.isEmpty()) {
+        trk->rte_name = str;
+      }
       break;
     case 6:
       trk->rte_urls.AddUrlLink(UrlLink(str));
@@ -1230,21 +1225,21 @@ parse_track_header()
 }
 
 static void
-parse_route_waypoint()
+parse_route_waypoint(const QStringList& lineparts)
 {
-  char* str;
   int column = -1;
   Waypoint* wpt = nullptr;
 
   bind_fields(rtept_header);
 
-  while ((str = csv_lineparse(nullptr, "\t", "", column++))) {
+  for (const auto& str : lineparts) {
+    column++;
     int field_no = header_fields[rtept_header][column];
     switch (field_no) {
     case 1:
-      is_fatal((*str == '\0'), MYNAME ": Route waypoint without name at line %d!\n", current_line);
+      is_fatal((str.isEmpty()), MYNAME ": Route waypoint without name at line %d!\n", current_line);
       wpt = find_waypt_by_name(str);
-      is_fatal((wpt == nullptr), MYNAME ": Route waypoint \"%s\" not in waypoint list (line %d)!\n", str, current_line);
+      is_fatal((wpt == nullptr), MYNAME ": Route waypoint \"%s\" not in waypoint list (line %d)!\n", qPrintable(str), current_line);
       wpt = new Waypoint(*wpt);
       break;
     }
@@ -1255,18 +1250,18 @@ parse_route_waypoint()
 }
 
 static void
-parse_track_waypoint()
+parse_track_waypoint(const QStringList& lineparts)
 {
-  char* str;
   int column = -1;
 
   bind_fields(trkpt_header);
   auto* wpt = new Waypoint;
 
-  while ((str = csv_lineparse(nullptr, "\t", "", column++))) {
+  for (const auto& str : lineparts) {
+    column++;
     double x;
 
-    if (! *str) {
+    if (str.isEmpty()) {
       continue;
     }
 
@@ -1304,7 +1299,7 @@ parse_track_waypoint()
       }
       break;
     case 9:
-      WAYPT_SET(wpt, course, atoi(str));
+      WAYPT_SET(wpt, course, atoi(CSTR(str)));
       break;
     }
   }
@@ -1354,37 +1349,35 @@ garmin_txt_read()
       continue;
     }
 
-    /* bail back to a (utf-8) char string, this format isn't worth the conversion work */
-    QByteArray utf8str = buff.toUtf8();
-    char* cin = csv_lineparse(utf8str.constData(), "\t", "", 0);
+    QStringList lineparts = csv_linesplit(buff, "\t", "", 0);
 
-    if (cin == nullptr) {
+    if (lineparts.size() < 1) {
       continue;
     }
-
-    if (case_ignore_strcmp(cin, "Header") == 0) {
-      parse_header();
-    } else if (case_ignore_strcmp(cin, "Grid") == 0) {
-      parse_grid();
-    } else if (case_ignore_strcmp(cin, "Datum") == 0) {
-      parse_datum();
-    } else if (case_ignore_strcmp(cin, "Waypoint") == 0) {
-      parse_waypoint();
-    } else if (case_ignore_strcmp(cin, "Route Waypoint") == 0) {
-      parse_route_waypoint();
-    } else if (case_ignore_strcmp(cin, "Trackpoint") == 0) {
-      parse_track_waypoint();
-    } else if (case_ignore_strcmp(cin, "Route") == 0) {
-      parse_route_header();
-    } else if (case_ignore_strcmp(cin, "Track") == 0) {
-      parse_track_header();
-    } else if (case_ignore_strcmp(cin, "Map") == 0) /* do nothing */ ;
+    auto linetype = lineparts.at(0);
+    lineparts.removeFirst();
+
+    if (case_ignore_strcmp(linetype, "Header") == 0) {
+      parse_header(lineparts);
+    } else if (case_ignore_strcmp(linetype, "Grid") == 0) {
+      parse_grid(lineparts);
+    } else if (case_ignore_strcmp(linetype, "Datum") == 0) {
+      parse_datum(lineparts);
+    } else if (case_ignore_strcmp(linetype, "Waypoint") == 0) {
+      parse_waypoint(lineparts);
+    } else if (case_ignore_strcmp(linetype, "Route Waypoint") == 0) {
+      parse_route_waypoint(lineparts);
+    } else if (case_ignore_strcmp(linetype, "Trackpoint") == 0) {
+      parse_track_waypoint(lineparts);
+    } else if (case_ignore_strcmp(linetype, "Route") == 0) {
+      parse_route_header(lineparts);
+    } else if (case_ignore_strcmp(linetype, "Track") == 0) {
+      parse_track_header(lineparts);
+    } else if (case_ignore_strcmp(linetype, "Map") == 0) /* do nothing */ ;
     else {
-      fatal(MYNAME ": Unknown identifier (%s) at line %d!\n", cin, current_line);
+      fatal(MYNAME ": Unknown identifier (%s) at line %d!\n", qPrintable(linetype), current_line);
     }
 
-    /* flush pending data */
-    while (csv_lineparse(nullptr, "\t", "", 0));
   }
 }
 
diff --git a/xcsv.cc b/xcsv.cc
index bd3886c0d54502cdb5b05ce82132d57cd8d58e68..20e3ad66536e992b2aeead75151aadc88bc0a3c0 100644 (file)
--- a/xcsv.cc
+++ b/xcsv.cc
@@ -245,7 +245,7 @@ XcsvStyle::xcsv_ofield_add(XcsvStyle* style, const QString& qkey, const QString&
 }
 
 QDateTime
-XcsvFormat::yyyymmdd_to_time(const char* s)
+XcsvFormat::yyyymmdd_to_time(const QString& s)
 {
   QDate d = QDate::fromString(s, "yyyyMMdd");
 #if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
@@ -387,7 +387,7 @@ void
 XcsvFormat::xcsv_parse_val(const QString& value, Waypoint* wpt, const XcsvStyle::field_map& fmp,
                            xcsv_parse_data* parse_data, const int line_no)
 {
-  const char* enclosure = "";
+  QString enclosure = "";
   geocache_data* gc_data = nullptr;
 
   if (fmp.printfc.isNull()) {
@@ -416,13 +416,13 @@ XcsvFormat::xcsv_parse_val(const QString& value, Waypoint* wpt, const XcsvStyle:
     /* IGNORE -- Calculated Sequence # For Output*/
     break;
   case XcsvStyle::XT_SHORTNAME:
-    wpt->shortname = csv_stringtrim(s, enclosure);
+    wpt->shortname = csv_stringtrim(value, enclosure, 0);
     break;
   case XcsvStyle::XT_DESCRIPTION:
-    wpt->description = csv_stringtrim(s, enclosure);
+    wpt->description = csv_stringtrim(value, enclosure, 0);
     break;
   case XcsvStyle::XT_NOTES:
-    wpt->notes = csv_stringtrim(s, "");
+    wpt->notes = value.trimmed();
     break;
   case XcsvStyle::XT_URL:
     if (!parse_data->link_) {
@@ -617,7 +617,7 @@ XcsvFormat::xcsv_parse_val(const QString& value, Waypoint* wpt, const XcsvStyle:
   }
   break;
   case XcsvStyle::XT_YYYYMMDD_TIME:
-    wpt->SetCreationTime(yyyymmdd_to_time(s));
+    wpt->SetCreationTime(yyyymmdd_to_time(value));
     break;
   case XcsvStyle::XT_GMT_TIME:
     wpt->SetCreationTime(sscanftime(s, fmp.printfc.constData(), 1));
@@ -649,7 +649,7 @@ XcsvFormat::xcsv_parse_val(const QString& value, Waypoint* wpt, const XcsvStyle:
   }
   break;
   case XcsvStyle::XT_GEOCACHE_LAST_FOUND:
-    wpt->AllocGCData()->last_found = yyyymmdd_to_time(s);
+    wpt->AllocGCData()->last_found = yyyymmdd_to_time(value);
     break;
 
   /* GEOCACHING STUFF ***************************************************/
@@ -663,10 +663,10 @@ XcsvFormat::xcsv_parse_val(const QString& value, Waypoint* wpt, const XcsvStyle:
     break;
   case XcsvStyle::XT_GEOCACHE_TYPE:
     /* Geocache Type */
-    wpt->AllocGCData()->type = gs_mktype(s);
+    wpt->AllocGCData()->type = gs_mktype(value);
     break;
   case XcsvStyle::XT_GEOCACHE_CONTAINER:
-    wpt->AllocGCData()->container = gs_mkcont(s);
+    wpt->AllocGCData()->container = gs_mkcont(value);
     break;
   case XcsvStyle::XT_GEOCACHE_HINT:
     wpt->AllocGCData()->hint = value.trimmed();
@@ -676,9 +676,9 @@ XcsvFormat::xcsv_parse_val(const QString& value, Waypoint* wpt, const XcsvStyle:
     break;
   case XcsvStyle::XT_GEOCACHE_ISAVAILABLE:
     gc_data = wpt->AllocGCData();
-    if (case_ignore_strcmp(csv_stringtrim(s, ""), "False") == 0) {
+    if (case_ignore_strcmp(value.trimmed(), "False") == 0) {
       gc_data->is_available = status_false;
-    } else if (case_ignore_strcmp(csv_stringtrim(s, ""), "True") == 0) {
+    } else if (case_ignore_strcmp(value.trimmed(), "True") == 0) {
       gc_data->is_available = status_true;
     } else {
       gc_data->is_available = status_unknown;
@@ -686,9 +686,9 @@ XcsvFormat::xcsv_parse_val(const QString& value, Waypoint* wpt, const XcsvStyle:
     break;
   case XcsvStyle::XT_GEOCACHE_ISARCHIVED:
     gc_data = wpt->AllocGCData();
-    if (case_ignore_strcmp(csv_stringtrim(s, ""), "False") == 0) {
+    if (case_ignore_strcmp(value.trimmed(), "False") == 0) {
       gc_data->is_archived = status_false;
-    } else if (case_ignore_strcmp(csv_stringtrim(s, ""), "True") == 0) {
+    } else if (case_ignore_strcmp(value.trimmed(), "True") == 0) {
       gc_data->is_archived = status_true;
     } else {
       gc_data->is_archived = status_unknown;
@@ -711,11 +711,11 @@ XcsvFormat::xcsv_parse_val(const QString& value, Waypoint* wpt, const XcsvStyle:
   case XcsvStyle::XT_GPS_FIX:
     wpt->fix = (fix_type)(atoi(s)-(fix_type)1);
     if (wpt->fix < fix_2d) {
-      if (!case_ignore_strcmp(s, "none")) {
+      if (!case_ignore_strcmp(value, "none")) {
         wpt->fix = fix_none;
-      } else if (!case_ignore_strcmp(s, "dgps")) {
+      } else if (!case_ignore_strcmp(value, "dgps")) {
         wpt->fix = fix_dgps;
-      } else if (!case_ignore_strcmp(s, "pps")) {
+      } else if (!case_ignore_strcmp(value, "pps")) {
         wpt->fix = fix_pps;
       } else {
         wpt->fix = fix_unknown;
@@ -724,13 +724,13 @@ XcsvFormat::xcsv_parse_val(const QString& value, Waypoint* wpt, const XcsvStyle:
     break;
   /* Tracks and routes *********************************************/
   case XcsvStyle::XT_ROUTE_NAME:
-    parse_data->rte_name = csv_stringtrim(s, enclosure);
+    parse_data->rte_name = csv_stringtrim(value, enclosure, 0);
     break;
   case XcsvStyle::XT_TRACK_NEW:
     parse_data->new_track = atoi(s);
     break;
   case XcsvStyle::XT_TRACK_NAME:
-    parse_data->trk_name = csv_stringtrim(s, enclosure);
+    parse_data->trk_name = csv_stringtrim(value, enclosure, 0);
     break;
 
   /* OTHER STUFF ***************************************************/
@@ -1667,34 +1667,31 @@ XcsvStyle::xcsv_parse_style_line(XcsvStyle* style, QString line)
   const QStringList tokens = tokenstr.split(',');
 
   if (op == u"FIELD_DELIMITER") {
-    auto cp = xcsv_get_char_from_constant_table(tokens[0]);
+    auto sp = csv_stringtrim(tokenstr, "\"", 1);
+    auto cp = xcsv_get_char_from_constant_table(sp);
     style->field_delimiter = cp;
 
-    char* p = csv_stringtrim(CSTR(style->field_delimiter), " ", 0);
     /* field delimiters are always bad characters */
-    if (0 == strcmp(p, "\\w")) {
-      style->badchars = " \n\r";
+    if (cp == u"\\w") {
+      style->badchars += " \n\r";
     } else {
-      style->badchars += p;
+      style->badchars += cp;
     }
-    xfree(p);
 
   } else if (op == u"FIELD_ENCLOSER") {
-    auto cp = xcsv_get_char_from_constant_table(tokens[0]);
+    auto sp = csv_stringtrim(tokenstr, "\"", 1);
+    auto cp = xcsv_get_char_from_constant_table(sp);
     style->field_encloser = cp;
 
-    char* p = csv_stringtrim(CSTR(style->field_encloser), " ", 0);
-    style->badchars += p;
-    xfree(p);
+    style->badchars += cp;
 
   } else if (op == u"RECORD_DELIMITER") {
-    auto cp = xcsv_get_char_from_constant_table(tokens[0]);
+    auto sp = csv_stringtrim(tokenstr, "\"", 1);
+    auto cp = xcsv_get_char_from_constant_table(sp);
     style->record_delimiter = cp;
 
     // Record delimiters are always bad characters.
-    auto* p = csv_stringtrim(CSTR(style->record_delimiter), " ", 0);
-    style->badchars += p;
-    xfree(p);
+    style->badchars += cp;
 
   } else if (op == u"FORMAT_TYPE") {
     if (tokens[0] == u"INTERNAL") {
@@ -1718,10 +1715,9 @@ XcsvStyle::xcsv_parse_style_line(XcsvStyle* style, QString line)
     style->whitespace_ok = tokens[0].toInt();
 
   } else if (op == u"BADCHARS") {
-    char* sp = csv_stringtrim(CSTR(tokenstr), "\"", 1);
-    QString cp = xcsv_get_char_from_constant_table(sp);
+    auto sp = csv_stringtrim(tokenstr, "\"", 1);
+    auto cp = xcsv_get_char_from_constant_table(sp);
     style->badchars += cp;
-    xfree(sp);
 
   } else if (op =="PROLOGUE") {
     style->prologue.append(tokenstr);
diff --git a/xcsv.h b/xcsv.h
index 56acc4cd23402c0d5f4421f3aae1fadfd2e1dd1c..4987b42651d5b77c2d367c30e23fabaf41cf582c 100644 (file)
--- a/xcsv.h
+++ b/xcsv.h
@@ -370,7 +370,7 @@ private:
 
   /* Member Functions */
 
-  static QDateTime yyyymmdd_to_time(const char* s);
+  static QDateTime yyyymmdd_to_time(const QString& s);
   static time_t sscanftime(const char* s, const char* format, int gmt);
   static time_t addhms(const char* s, const char* format);
   static QString writetime(const char* format, time_t t, bool gmt);